package gbench.sandbox.plot.geom;

import java.util.Arrays;
import java.util.function.Function;
import java.util.stream.Collectors;
import java.util.stream.Stream;

import org.jblas.DoubleMatrix;
import gbench.common.matlib.MatlibCanvas;
import gbench.common.matlib.MatlibCanvas.NVec;
import gbench.common.matlib.MatlibCanvas.Range;

import static java.lang.Math.PI;
import static java.lang.Math.cos;
import static java.lang.Math.sin;
import static gbench.common.tree.LittleTree.compose_f;
import static gbench.common.tree.LittleTree.DISTINCT_BY_KEY;
import static gbench.sandbox.plot.geom.JunitEllipse.Mx.*;

/**
 * 绘制几何图形，制作一个椭圆，由一条直线，转成一个椭圆
 * @author gbench
 *
 */
public class JunitEllipse {
    
    /**
     * 简单矩阵
     * @author gbench
     *
     */
    public static class Mx {
        /**
         * 
         * @param dblmx
         * @return
         */
        public static double[][] unbox(final Double[][] dblmx) {
            final var m = dblmx.length;
            final var mm = new double[m][];
            for (int i = 0; i < m; i++)
                mm[i] = unbox(dblmx[i]);
            return mm;
        }

        /**
         * null 会被视为0
         * 
         * @param dd
         * @return
         */
        public static double[] unbox(final Double[] dd) {
            final var n = dd.length;
            final var nn = new double[n];
            for (int j = 0; j < n; j++)
                nn[j] = dd[j] == null ? 0d : dd[j];
            return nn;
        }

        /**
         * 一维向量
         * 
         * @param dd
         * @return
         */
        public static DoubleMatrix dblmx(Number... dd) {
            final var mm = Arrays.stream(dd).mapToDouble(e -> e.doubleValue()).toArray();
            return new DoubleMatrix(mm);
        }

        /**
         * 
         * @param dd
         * @return
         */
        public static DoubleMatrix dblmx(final Double[][] dd) {
            return new DoubleMatrix(unbox(dd));
        }

        /**
         * 对角阵
         */
        public static DoubleMatrix diag(Number... dd) {
            return DoubleMatrix.diag(dblmx(dd));
        }
        
        /**
         * 生成一个5*5的[R,I] 旋转矩阵(左乘结构)    <br>
         * R 是一个2x2维的旋转子矩阵:<br>
         * cos(theta)    -sin(theta)        <br>
         * sin(theta)    cos(theta)        <br>
         * I 是一个3x3维的单位矩阵。             <br>
         * @param <T> 返回值的类型即mapper的结果类型
         * @param theta 旋转角度(正方向为逆时针方向)
         * @param mapper 矩阵变换器 : mx 5*5矩阵 -> t
         * @return 旋转矩阵[R,I]
         */
        public static <T> T rotateMx(final double theta, final Function<Double[][],T> mapper) {
            
            final var mx = NVec.of( // 旋转矩阵
                //列向量的变换分块矩阵:坐标旋转
                cos(theta), -sin(theta),     0d,0d,0d,
                sin(theta), cos(theta),      0d,0d,0d,
                // 列向量的保留分块矩阵:颜色数据矩阵
                0d,         0d,              1d,0d,0d,
                0d,         0d,              0d,1d,0d,
                0d,         0d,              0d,0d,1d
            ).reshape(Double[][].class,5, 5);// 随机生成一个矩阵
            
            return mapper.apply(mx);
        }

        /**
         * 核心函数
         * 
         * @param <T>    mapper 的 值类型
         * @param matrix 浮点类型的矩阵
         * @param mapper 行变换函数
         * @return T类型的流
         */
        public static <T> Stream<T> stream(final DoubleMatrix matrix, final Function<double[], T> mapper) {
            return Arrays.stream(matrix.toArray2()).map(mapper);
        }

    }
    
    /**
     * 绘制一个椭圆
     * @param args
     */
    public static void main(String args[]) {
        final var size = 1000; // 画布的绘图范围
        final var matlib = new MatlibCanvas(size);
        matlib.plot(size, size/2, pen->{
            pen.drawPoints2(Range.of(100,200).limit(500).map(x->matlib.xY(x,0)), // 画出一条水平线，然后给与拉伸变换。
                compose_f(DoubleMatrix::new,NVec::toArray), pointMx -> { // 把这条 直线转换成一个 点向量矩阵
                return Range.of(-PI, PI).limit(1300).flatMap( theta -> stream ( // 把这条直线[点向量矩阵]旋转1个圆周：一周被分解成2000分
                    pointMx.mmul(rotateMx(theta, Mx::dblmx).transpose()) // 旋转
                        .mmul(diag(1d, 1d / 3, 1d, 1d, 1d)) // 缩放坐标轴,注意为保持没漏洞只能缩，不能放,因为被离散后就不连续了。
                        .addRowVector(dblmx(300d, 200d, 0d, 0d, 0d)) // 平移
                        ,compose_f(NVec::ints,NVec::new)).filter(DISTINCT_BY_KEY(Object::toString)) // 去重
                ).collect(Collectors.toList());
            });// drawPoints2
        });
    }

}
